"""
User Service
用户服务 - 用户注册、登录、资料管理
"""

import uuid
from typing import Optional, List, Dict, Any
from datetime import datetime, timedelta

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func
from sqlalchemy.orm import selectinload
import structlog
from passlib.context import CryptContext

from ..models.user import User
from ..models.user_settings import UserSettings
from ..core.database import DatabaseSession
from ..core.config import settings
from ..core.cache import cache_result, create_cache_key
from ..core.exceptions import (
    UserNotFoundError,
    UserAlreadyExistsError,
    InvalidCredentialsError,
    UserAccountLockedError,
    ValidationError,
)

# 配置结构化日志
logger = structlog.get_logger(__name__)

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class UserService:
    """用户服务类"""

    def __init__(self, db_session: DatabaseSession):
        self.db_session = db_session

    async def create_user(
        self,
        email: str,
        username: str,
        password: str,
        full_name: Optional[str] = None,
        role: str = "user",
        **kwargs,
    ) -> User:
        """
        创建新用户

        Args:
            email: 用户邮箱
            username: 用户名
            password: 密码
            full_name: 全名
            role: 用户角色
            **kwargs: 其他参数

        Returns:
            User: 创建的用户对象

        Raises:
            UserAlreadyExistsError: 用户已存在
            ValidationError: 参数验证失败
        """
        logger.info("创建新用户", email=email, username=username)

        # 验证输入参数
        await self._validate_user_input(email, username, password)

        async with self.db_session as session:
            # 检查用户是否已存在
            existing_user = await self._get_user_by_email_or_username(
                session, email, username
            )
            if existing_user:
                if existing_user.email == email:
                    raise UserAlreadyExistsError(f"邮箱 {email} 已被使用")
                else:
                    raise UserAlreadyExistsError(f"用户名 {username} 已被使用")

            # 创建用户
            hashed_password = self._hash_password(password)
            user = User(
                email=email.lower(),
                username=username.lower(),
                full_name=full_name,
                hashed_password=hashed_password,
                role=role,
                status="active",
                **kwargs,
            )

            session.add(user)
            await session.flush()

            # 创建用户设置
            user_settings = UserSettings(user_id=user.id)
            session.add(user_settings)

            await session.commit()
            await session.refresh(user, ["settings"])

            logger.info("用户创建成功", user_id=str(user.id), username=username)
            return user

    async def authenticate_user(self, email_or_username: str, password: str) -> User:
        """
        用户认证

        Args:
            email_or_username: 邮箱或用户名
            password: 密码

        Returns:
            User: 认证成功的用户

        Raises:
            InvalidCredentialsError: 凭据无效
            UserAccountLockedError: 账户被锁定
            UserNotFoundError: 用户不存在
        """
        logger.info("用户认证", identifier=email_or_username)

        async with self.db_session as session:
            # 查找用户
            user = await self._get_user_by_email_or_username(
                session, email_or_username, email_or_username
            )

            if not user:
                logger.warning("用户不存在", identifier=email_or_username)
                raise UserNotFoundError("用户不存在")

            # 检查账户状态
            if user.status == "locked":
                logger.warning("账户被锁定", user_id=str(user.id))
                raise UserAccountLockedError("账户已被锁定，请联系管理员")

            if user.status == "banned":
                logger.warning("账户被封禁", user_id=str(user.id))
                raise UserAccountLockedError("账户已被封禁")

            # 验证密码
            if not self._verify_password(password, user.hashed_password):
                logger.warning("密码验证失败", user_id=str(user.id))
                raise InvalidCredentialsError("密码错误")

            # 更新最后登录时间
            user.last_login_at = datetime.utcnow()
            await session.commit()

            logger.info("用户认证成功", user_id=str(user.id), username=user.username)
            return user

    @cache_result(cache_name="user_sessions", ttl=1800)  # 30分钟缓存
    async def get_user_by_id(self, user_id: uuid.UUID) -> Optional[User]:
        """
        根据ID获取用户 - 带缓存版本

        Args:
            user_id: 用户ID

        Returns:
            User: 用户对象，不存在则返回None
        """
        async with self.db_session as session:
            result = await session.execute(
                select(User)
                .where(User.id == user_id)
                .options(selectinload(User.settings))
            )
            return result.scalar_one_or_none()

    async def get_user_by_email(self, email: str) -> Optional[User]:
        """
        根据邮箱获取用户

        Args:
            email: 用户邮箱

        Returns:
            User: 用户对象，不存在则返回None
        """
        async with self.db_session as session:
            result = await session.execute(
                select(User)
                .where(func.lower(User.email) == email.lower())
                .options(selectinload(User.settings))
            )
            return result.scalar_one_or_none()

    async def get_user_by_username(self, username: str) -> Optional[User]:
        """
        根据用户名获取用户

        Args:
            username: 用户名

        Returns:
            User: 用户对象，不存在则返回None
        """
        async with self.db_session as session:
            result = await session.execute(
                select(User)
                .where(func.lower(User.username) == username.lower())
                .options(selectinload(User.settings))
            )
            return result.scalar_one_or_none()

    async def update_user_profile(
        self,
        user_id: uuid.UUID,
        full_name: Optional[str] = None,
        avatar_url: Optional[str] = None,
        bio: Optional[str] = None,
        **kwargs,
    ) -> User:
        """
        更新用户资料

        Args:
            user_id: 用户ID
            full_name: 全名
            avatar_url: 头像URL
            bio: 个人简介
            **kwargs: 其他参数

        Returns:
            User: 更新后的用户对象

        Raises:
            UserNotFoundError: 用户不存在
        """
        logger.info("更新用户资料", user_id=str(user_id))

        async with self.db_session as session:
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            # 更新字段
            if full_name is not None:
                user.full_name = full_name
            if avatar_url is not None:
                user.avatar_url = avatar_url
            if bio is not None:
                user.bio = bio

            # 更新其他字段
            for key, value in kwargs.items():
                if hasattr(user, key):
                    setattr(user, key, value)

            await session.commit()
            await session.refresh(user, ["settings"])

            logger.info("用户资料更新成功", user_id=str(user_id))
            return user

    async def update_user_settings(
        self, user_id: uuid.UUID, **settings_kwargs
    ) -> UserSettings:
        """
        更新用户设置

        Args:
            user_id: 用户ID
            **settings_kwargs: 设置参数

        Returns:
            UserSettings: 更新后的用户设置

        Raises:
            UserNotFoundError: 用户不存在
        """
        logger.info("更新用户设置", user_id=str(user_id))

        async with self.db_session as session:
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            if not user.settings:
                user.settings = UserSettings(user_id=user_id)

            # 更新设置字段
            for key, value in settings_kwargs.items():
                if hasattr(user.settings, key):
                    setattr(user.settings, key, value)

            await session.commit()

            logger.info("用户设置更新成功", user_id=str(user_id))
            return user.settings

    async def change_password(
        self, user_id: uuid.UUID, current_password: str, new_password: str
    ) -> bool:
        """
        修改密码

        Args:
            user_id: 用户ID
            current_password: 当前密码
            new_password: 新密码

        Returns:
            bool: 是否成功

        Raises:
            UserNotFoundError: 用户不存在
            InvalidCredentialsError: 当前密码错误
            ValidationError: 新密码验证失败
        """
        logger.info("修改用户密码", user_id=str(user_id))

        # 验证新密码
        self._validate_password(new_password)

        async with self.db_session as session:
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            # 验证当前密码
            if not self._verify_password(current_password, user.hashed_password):
                raise InvalidCredentialsError("当前密码错误")

            # 更新密码
            user.hashed_password = self._hash_password(new_password)
            await session.commit()

            logger.info("用户密码修改成功", user_id=str(user_id))
            return True

    async def reset_password(self, user_id: uuid.UUID, new_password: str) -> bool:
        """
        重置密码（管理员操作）

        Args:
            user_id: 用户ID
            new_password: 新密码

        Returns:
            bool: 是否成功

        Raises:
            UserNotFoundError: 用户不存在
            ValidationError: 密码验证失败
        """
        logger.info("重置用户密码", user_id=str(user_id))

        # 验证新密码
        self._validate_password(new_password)

        async with self.db_session as session:
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            # 更新密码
            user.hashed_password = self._hash_password(new_password)
            await session.commit()

            logger.info("用户密码重置成功", user_id=str(user_id))
            return True

    async def update_user_status(
        self, user_id: uuid.UUID, status: str, reason: Optional[str] = None
    ) -> User:
        """
        更新用户状态（管理员操作）

        Args:
            user_id: 用户ID
            status: 新状态 (active, locked, banned)
            reason: 原因

        Returns:
            User: 更新后的用户

        Raises:
            UserNotFoundError: 用户不存在
            ValidationError: 状态无效
        """
        logger.info("更新用户状态", user_id=str(user_id), status=status, reason=reason)

        valid_statuses = ["active", "locked", "banned"]
        if status not in valid_statuses:
            raise ValidationError(f"无效的用户状态: {status}")

        async with self.db_session as session:
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            user.status = status
            await session.commit()

            logger.info("用户状态更新成功", user_id=str(user_id), status=status)
            return user

    async def get_users(
        self,
        skip: int = 0,
        limit: int = 100,
        status: Optional[str] = None,
        role: Optional[str] = None,
        search: Optional[str] = None,
    ) -> List[User]:
        """
        获取用户列表（管理员功能）

        Args:
            skip: 跳过数量
            limit: 限制数量
            status: 状态过滤
            role: 角色过滤
            search: 搜索关键词

        Returns:
            List[User]: 用户列表
        """
        async with self.db_session as session:
            query = select(User).options(selectinload(User.settings))

            # 应用过滤条件
            if status:
                query = query.where(User.status == status)
            if role:
                query = query.where(User.role == role)
            if search:
                search_filter = or_(
                    User.email.ilike(f"%{search}%"),
                    User.username.ilike(f"%{search}%"),
                    User.full_name.ilike(f"%{search}%"),
                )
                query = query.where(search_filter)

            # 排序和分页
            query = query.order_by(User.created_at.desc()).offset(skip).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def get_user_statistics(self, user_id: uuid.UUID) -> Dict[str, Any]:
        """
        获取用户统计信息

        Args:
            user_id: 用户ID

        Returns:
            Dict[str, Any]: 统计信息
        """
        async with self.db_session as session:
            # 基础用户信息
            user = await self.get_user_by_id(user_id)
            if not user:
                raise UserNotFoundError(f"用户 {user_id} 不存在")

            # 获取统计信息
            from ..models.creation_request import CreationRequest
            from ..models.generated_content import GeneratedContent

            # 创作请求统计
            requests_result = await session.execute(
                select(
                    func.count(CreationRequest.id),
                    func.count(CreationRequest.id).filter(
                        CreationRequest.status == "completed"
                    ),
                ).where(CreationRequest.user_id == user_id)
            )
            total_requests, completed_requests = requests_result.first()

            # 生成内容统计
            contents_result = await session.execute(
                select(
                    func.count(GeneratedContent.id),
                    func.count(GeneratedContent.id).filter(
                        GeneratedContent.status == "completed"
                    ),
                    func.sum(GeneratedContent.file_size_bytes),
                ).where(GeneratedContent.user_id == user_id)
            )
            total_contents, completed_contents, total_storage = contents_result.first()

            return {
                "user_id": str(user_id),
                "username": user.username,
                "email": user.email,
                "role": user.role,
                "status": user.status,
                "created_at": user.created_at.isoformat() if user.created_at else None,
                "last_login_at": user.last_login_at.isoformat()
                if user.last_login_at
                else None,
                "storage_usage": {
                    "used_mb": user.used_storage_mb or 0,
                    "quota_mb": user.storage_quota_mb or 0,
                    "usage_percent": user.storage_usage_percent,
                },
                "generation_stats": {
                    "total_requests": total_requests or 0,
                    "completed_requests": completed_requests or 0,
                    "total_contents": total_contents or 0,
                    "completed_contents": completed_contents or 0,
                    "total_storage_mb": round((total_storage or 0) / (1024 * 1024), 2),
                },
                "daily_limit": {
                    "current_limit": user.daily_generation_limit,
                    "custom_limit": user.settings.effective_daily_limit
                    if user.settings
                    else None,
                },
            }

    # 私有辅助方法

    async def _validate_user_input(
        self, email: str, username: str, password: str
    ) -> None:
        """验证用户输入"""
        # 验证邮箱格式
        import re

        email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        if not re.match(email_pattern, email):
            raise ValidationError("无效的邮箱格式")

        # 验证用户名格式
        if len(username) < 3 or len(username) > 50:
            raise ValidationError("用户名长度必须在3-50个字符之间")

        if not username.replace("_", "").replace("-", "").isalnum():
            raise ValidationError("用户名只能包含字母、数字、下划线和连字符")

        # 验证密码强度
        self._validate_password(password)

    def _validate_password(self, password: str) -> None:
        """验证密码强度"""
        if len(password) < settings.security.password_min_length:
            raise ValidationError(
                f"密码长度至少为 {settings.security.password_min_length} 个字符"
            )

        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)

        if settings.security.password_require_uppercase and not has_upper:
            raise ValidationError("密码必须包含大写字母")
        if settings.security.password_require_lowercase and not has_lower:
            raise ValidationError("密码必须包含小写字母")
        if settings.security.password_require_numbers and not has_digit:
            raise ValidationError("密码必须包含数字")
        if settings.security.password_require_special_chars and not has_special:
            raise ValidationError("密码必须包含特殊字符")

    def _hash_password(self, password: str) -> str:
        """哈希密码"""
        return pwd_context.hash(password)

    def _verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)

    async def _get_user_by_email_or_username(
        self,
        session: AsyncSession,
        email: Optional[str] = None,
        username: Optional[str] = None,
    ) -> Optional[User]:
        """根据邮箱或用户名获取用户"""
        conditions = []
        if email:
            conditions.append(func.lower(User.email) == email.lower())
        if username:
            conditions.append(func.lower(User.username) == username.lower())

        if not conditions:
            return None

        result = await session.execute(
            select(User).where(or_(*conditions)).options(selectinload(User.settings))
        )
        return result.scalar_one_or_none()
